/*
Copyright (C) 2011 The University of Michigan

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Please send inquiries to powertutor@umich.edu
 */

package vn.cybersoft.obs.andriod.batterystats2.components;

import vn.cybersoft.obs.andriod.batterystats2.service.IterationData;
import android.os.SystemClock;
import android.util.Log;

public abstract class PowerComponent extends Thread {
	private final String TAG = "PowerComponent";

	/*
	 * Extending classes need to override the calculateIteration function. It
	 * should calculate the data point for the given component in a timely
	 * manner (under 1 second, longer times will cause data to be missed). The
	 * iteration parameter can be ignored in most cases.
	 */
	protected abstract IterationData calculateIteration(long iteration);

	/*
	 * Extending classes should provide a recognizable name for this component
	 * to be used when writing to logs.
	 */
	public abstract String getComponentName();

	/*
	 * Returns true if this component collects usage information per uid.
	 */
	public boolean hasUidInformation() {
		return false;
	}

	/*
	 * Called when the thread running this interface is asked to exit.
	 */
	protected void onExit() {
	}

	/*
	 * In general we should only need to buffer two data elements.
	 */
	private IterationData data1;
	private IterationData data2;
	private long iteration1;
	private long iteration2;

	protected long beginTime;
	protected long iterationInterval;

	public PowerComponent() {
		setDaemon(true);
	}

	/*
	 * This is called once at the begginning of the daemon loop.
	 */
	public void init(long beginTime, long iterationInterval) {
		this.beginTime = beginTime;
		this.iterationInterval = iterationInterval;
		data1 = data2 = null;
		iteration1 = iteration2 = -1;
	}

	/* Runs the daemon loop that collects data for this component. */
	public void run() {
		android.os.Process
				.setThreadPriority(android.os.Process.THREAD_PRIORITY_MORE_FAVORABLE);
		for (long iter = 0; !Thread.interrupted();) {
			/*
			 * Hand off to the client class to actually calculate the
			 * information we want for this component.
			 */
			IterationData data = calculateIteration(iter);
			if (data != null) {
				synchronized (this) {
					if (iteration1 < iteration2) {
						iteration1 = iter;
						data1 = data;
					} else {
						iteration2 = iter;
						data2 = data;
					}
				}
			}
			if (interrupted()) {
				break;
			}

			long curTime = SystemClock.elapsedRealtime();
			/* Compute the next iteration that we can make the start of. */
			long oldIter = iter;
			iter = (long) Math.max(iter + 1, 1 + (curTime - beginTime)
					/ iterationInterval);
			if (oldIter + 1 != iter) {
				Log.w(TAG, "[" + getComponentName()
						+ "] Had to skip from iteration " + oldIter + " to "
						+ iter);
			}
			/* Sleep until the next iteration completes. */
			try {
				sleep(beginTime + iter * iterationInterval - curTime);
			} catch (InterruptedException e) {
				break;
			}
		}
		onExit();
	}

	/*
	 * Returns the data point for the given iteration. This method will be
	 * called with a strictly increasing iteration parameter.
	 */
	public IterationData getData(long iteration) {
		synchronized (this) {
			IterationData ret = null;
			if (iteration == iteration1)
				ret = data1;
			if (iteration == iteration2)
				ret = data2;
			if (iteration1 <= iteration) {
				data1 = null;
				iteration1 = -1;
			}
			if (iteration2 <= iteration) {
				data2 = null;
				iteration2 = -1;
			}
			if (ret == null) {
				Log.w(TAG, "[" + getComponentName()
						+ "] Could not find data for " + "requested iteration");
			}
			return ret;
		}
	}
}
